/***************************************************************************
 * This program is free software; you can redistribute it and/or           *
 * modify it under the terms of the GNU General Public License             *
 * as published by the Free Software Foundation; either version 2          *
 * of the License, or (at your option) any later version.                  *
 *                                                                         *
 * This program is distributed in the hope that it will be useful,         *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           *
 * GNU General Public License for more details.                            *
 *                                                                         *
 * You should have received a copy of the GNU General Public License       *
 * along with this program; if not, write to the Free Software Foundation, *
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.         *
 *                                                                         *
 *   Copyright (C) 2007,2008 by Ivan Vucica                                *
 *   ivucica@gmail.com                                                     *
 ***************************************************************************/



#ifndef __TOKEN_H
#define __TOKEN_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string>
#include <sstream>
#include "logocontext.h"
class Token {
	public:

		typedef enum {
			TT_WORD,
			TT_NUMBER,
			TT_EXPRESSION,
			TT_LIST,
			TT_OPERATOR,
			TT_PROCEDURE,
			TT_VARIABLE,
			TT_NL,
			TT_TO, // TODO
			TT_END, // TODO
			TT_EOF,

			TT_UNDEFINED
		} tokentype_t;


		Token () {
			type = TT_UNDEFINED;
		}
		Token (tokentype_t tt) {
			type = tt;
		}
		Token (std::string word) {
			number = 4900;
			op = OP_UNDEFINED;

			switch (word[0]) {
			default:
				type = TT_PROCEDURE;
				text = word;
				ToNumber();
				ToOperator();
				ToTo();
				ToEnd();
				//printf("[%c]\n", word[0]);
/*				if (type==TT_PROCEDURE)
					printf("Procedure call %s\n", word.c_str());
				if (type==TT_OPERATOR)
					printf("Operator %s\n", word.c_str());
				if (type==TT_NUMBER)
					printf("Number %s\n", word.c_str());*/
				break;
			case '"':
				type = TT_WORD;
				text = word.substr(1);
				ToNumber();
				/*if (type==TT_NUMBER)
					printf("Number from word %s\n", word.c_str());
				else
					printf("Word %s\n", text.c_str());*/
				break;
			case '\n':
				type = TT_NL;
				break;
			case '[':
				type = TT_LIST;
				text = word;
				break;
			case ':':
				type = TT_VARIABLE;
				text = word.substr(1);
				break;
			case '(':
				type = TT_EXPRESSION;
				text = word;
				break;
			}
		}
		Token (tokentype_t tt, std::string text) {
			type = tt;
			this->text = text;
		}

		void ToTo() {
			if (text == "TO") type = TT_TO;
		}
		void ToEnd() {
			if (text == "END") type = TT_END;
		}
		void ToOperator() {
			if (text == "+" ||
			    text == "-" ||
			    text == "*" ||
			    text == "/" ||
			    text == "=") {
			    	type = TT_OPERATOR;
					if (text == "+") op = OP_PLUS; else
					if (text == "-") op = OP_MINUS; else
					if (text == "*") op = OP_MULTIPLY; else
					if (text == "/") op = OP_DIVIDE; else
					if (text == "=") op = OP_EQUALS;
			    }
		}
		void ToNumber() {
			/// Converts a word into a number of double precision,
			/// if possible, and changes the type accordingly.
			/// If not possible, the token remains a word.


			bool decimal=false;
			for (unsigned int i = 0 ; i < text.size() ; i++) {
				if ((text[i] >= '0' && text[i] <= '9') || text[i] == '.' || text[i] == '-') {
					if (text[i] == '.') if (!decimal) decimal = true;
						else return;
					// if we already encountered a decimal point, and we encounter it again,
					// it's not a number.
				} else {
					return;
				}
			}
			// if we reached this point, then it's a number
			sscanf(text.c_str(), "%lf", &number);
			type = TT_NUMBER;
		}

		std::string Describe() const {
			std::stringstream s;
			switch (type) {
				case TT_UNDEFINED:
					return ("UNDEFINED");
					break;
				case TT_WORD:
					return std::string("WORD: ") + text;
					break;
				case TT_OPERATOR:
					s << "OP: ";
					switch (op) {
						case OP_PLUS: s << "+"; break;
						case OP_MINUS: s << "-"; break;
						case OP_MULTIPLY: s << "*"; break;
						case OP_DIVIDE: s << "/"; break;
						case OP_EQUALS: s << "="; break;
						default: s << op;
					}
					return s.str();
				case TT_EXPRESSION:
					return std::string("EXPRESSION: ") + text;
					break;
				case TT_LIST:
					return std::string("LIST: ") + text;
					break;
				case TT_NUMBER:
					s << "NUMBER: " << number;
					return s.str();
					break;
				case TT_PROCEDURE:
					return std::string("PROCEDURE: ") + text;
					break;
				case TT_VARIABLE:
					return std::string("VARIABLE: ") + text;
					break;
				case TT_NL:
					return "NEWLINE";
					break;
				case TT_EOF:
					return "EOF";
					break;
				case TT_TO:
					return "TO";
					break;
				case TT_END:
					return "END";
					break;
				default:
					return "BAD TOKEN";
					break;
			}
			return "CODEFLOW ERROR";
		}
		void Dump() const {
			printf("%s", Describe().c_str());
		}
		bool operator==(Token t) const {
			if (type != t.type)
				return false;
			switch (type) {
				case TT_WORD:
					return text == t.text;
				case TT_LIST: // FIXME (Khaos#1#): Not necessarily true! For example, if a list contains extra spaces, its "original text" will be different, but its contents are still the same.
					return text == t.text;
				case TT_NUMBER:
					return number == t.number;
				default:
					throw std::string("Something is comparing tokens that usually dont need comparing");
			}
		}

		Token Evaluate(LogoContext& global, LogoContext& local ) const;


		tokentype_t type;
		std::string text;
		double number;

		enum {
			OP_UNDEFINED=0,
			OP_PLUS,
			OP_MINUS,
			OP_MULTIPLY,
			OP_DIVIDE,
			OP_EQUALS
		} op;
};

#endif
